package com.game;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Random;

import org.apache.log4j.Logger;

import com.client.NioClient;
import com.client.ResponseHandler;
import com.common.game.Bid;
import com.common.game.Player;
import com.common.utilities.SendData;
import com.game.utilities.CommandArguments;

/**
 * User: wattsc5
 * <p/>
 * Date: 2/28/12
 */
public class ClientGame
{
	private static final Logger logger = Logger.getLogger(ClientGame.class);

	private NioClient client;

	private Player player;

	private ResponseHandler responseHandler;

	private ClientGameAPI clientGameAPI;

	private ClientCommandsAPI clientCommandsAPI;

	private boolean useAI;

	private Bid lastBid;

	private static final long AIWaitTime = 2000;

	private AIState previousAIState;

	/**
	 * Initializes a new instance of a ClientGame.
	 */
	public ClientGame()
	{
	}

	public Player getPlayer()
	{
		return player;
	}

	public void setPlayer(Player player)
	{

		this.player = player;
	}

	public void initializeGame(ClientGameAPI api, String hostAddress, int port, boolean useAI)
	{
		this.useAI = useAI;
		this.clientCommandsAPI = new ClientCommandsAPI(this);
		this.clientGameAPI = api;
		this.responseHandler = new ResponseHandler(api);

		try
		{
			this.client = new NioClient(InetAddress.getByName(hostAddress), port);
		}
		catch (UnknownHostException e)
		{
			logger.error(e);
		}
		new Thread(this.client).start();
	}

	public void start()
	{
		if (!this.useAI)
		{
			this.promptPlayerName();
			this.promptMainLoop();
		}
		else
		{
			SendData sendData = new SendData();
			sendData.addArgument("join");
			Random random = new Random();

			sendData.addArgument("AI" + random.nextInt(50) + 1);
			this.sendData(sendData);
		}
	}

	public void quit()
	{
		logger.info("Quitting game");
		SendData quitData = new SendData();
		quitData.addArgument("quit");
		quitData.addArgument(this.getPlayer().getName());
		this.sendData(quitData);
	}

	public void sendData(SendData data)
	{
		this.sendData(data.getDataAsString());
	}

	public void sendData(String data)
	{
		this.sendData(data.getBytes());
	}

	public void sendData(byte[] data)
	{
		try
		{
			client.send(data, this.responseHandler);
		}
		catch (IOException e)
		{
			logger.error(e);
		}
	}

	private void promptPlayerName()
	{
		String input = this.promptMessage("Username:");
		SendData sendData = new SendData();
		sendData.addArgument("join");
		sendData.addArgument(input);
		this.sendData(sendData);
	}

	public void sendMessageToConsole(String string)
	{
		System.out.print(string);
	}

	private String promptMessage(String message)
	{
		this.sendMessageToConsole(message);
		return this.getUserInput();
	}

	private void promptMainLoop()
	{
		String input = "";
		while (!input.equals("quit"))
		{
			input = this.promptMessage("");

			if (input.isEmpty())
			{
				continue;
			}

			try
			{
				CommandArguments arguments = new CommandArguments(input);
				logger.debug("Calling - " + arguments.getCommand());
				Class.forName("com.game.ClientCommandsAPI").getMethod(arguments.getCommand(), CommandArguments.class).invoke(this.clientCommandsAPI, arguments);
			}
			catch (IllegalAccessException e)
			{
				logger.error(e);
			}
			catch (InvocationTargetException e)
			{
				logger.error(e);
			}
			catch (NoSuchMethodException e)
			{
				logger.warn(e);
				System.out.println("Invalid command");
			}
			catch (ClassNotFoundException e)
			{
				logger.error(e);
			}
		}
	}

	private String getUserInput()
	{
		String string = "";
		char c = 'a';

		while (true)
		{
			try
			{
				c = (char) System.in.read();

				if (c == '\n')
				{
					break;
				}
			}
			catch (IOException e)
			{
				logger.error(e);
			}
			string += c;
		}

		return string;
	}


	public void changeAIState(AIState state)
	{
		if (!this.useAI)
		{
			return;
		}

		try
		{
			Thread.sleep(this.AIWaitTime);
		}
		catch (InterruptedException e)
		{
			logger.error(e);
		}

		logger.debug("Changing AI State to " + state.toString());

		CommandArguments args;
		switch (state)
		{
			case PlayerTurn:
				if (!this.getPlayer().isInGame())
				{
					break;
				}
				int diceCount = 1;
				int diceValue = 6;
				if (this.lastBid != null)
				{
					diceCount = this.lastBid.getDiceCount() + 1;
					if (this.lastBid.getDiceCount() == 6 ||
						diceCount > this.player.getDiceCount())
					{
						this.changeAIState(AIState.Challenge);
						return;
					}
				}

				logger.trace("AI bidding Count:" + diceCount + " Value:" + diceValue);
				args = new CommandArguments("bid " + diceCount + " " + diceValue);
				this.clientCommandsAPI.bid(args);
				break;
			case InGame:
				this.getPlayer().setInGame(true);
				break;
			case InLobby:
				break;
			case Kicked:
				this.getPlayer().setInGame(false);
				break;
			case InvalidMove:
				if (this.previousAIState == null)
				{
					break;
				}

				if (this.previousAIState == AIState.Challenge)
				{
					this.changeAIState(AIState.Challenge);
					break;
				}

				if (this.previousAIState == AIState.PlayerTurn)
				{
					this.changeAIState(AIState.PlayerTurn);
					break;
				}

				break;
			case Challenge:
				if (!this.getPlayer().isInGame())
				{
					break;
				}
				logger.trace("AI challenging");
				args = new CommandArguments("challenge");
				this.clientCommandsAPI.challenge(args);
				break;
		}

		this.previousAIState = state;
	}

	public void setLastBid(Bid bid)
	{
		this.lastBid = bid;
	}
}
